﻿using Genius.Brain.Shared.Strategy;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Genius.Brain.Shared.BO;
using Genius.Brain.Shared.Locator;
using System.Globalization;

namespace Genius.Brain.Impl.Strategy
{
    class TextMatcherStrategy : ITextMatcherStrategy
    {
        private ITextTokenizerStrategy _textTokenizerStrategy;
        public TextMatcherStrategy(ITextTokenizerStrategy textTokenizerStrategy)
        {
            _textTokenizerStrategy = textTokenizerStrategy;
        }
        public IList<IMatch> Match(string pattern, string text)
        {
            var patternToken = _textTokenizerStrategy.Tokenize(pattern);
            var textToken = _textTokenizerStrategy.Tokenize(text);

            var matches = new List<IMatch>();
           
            int pi = 0;
            int ti = 0;
            

            while(pi < patternToken.Count || ti < textToken.Count)
            {
                ITextToken pToken = null;
                ITextToken tToken = null;
                if (pi < patternToken.Count)
                    pToken = patternToken[pi++];
                if (ti < textToken.Count)
                    tToken = textToken[ti++];

                //compare
                if (pToken == null) //tToken != null
                {
                    //Extra
                    var match = BrainLocator.GetMatch();
                    match.MatchType = MatchType.Extra;
                    match.Pattern = null;
                    match.Word = tToken;
                    matches.Add(match);
                }
                else if (tToken == null) //pToken != null
                {
                    //Missing
                    var match = BrainLocator.GetMatch();
                    match.MatchType = MatchType.Missing;
                    match.Pattern = pToken;
                    match.Word = null;
                    matches.Add(match);
                }
                else
                {

                    var match = diff(pToken, tToken);
                    if(match != null)
                    {
                        matches.Add(match);
                        continue;
                    }

                    if (pi < patternToken.Count)
                    {
                        //Pattern LookAhead
                        match = diff(patternToken[pi], tToken);
                        if (match != null)
                        {
                            //Token passt zu nächstem Pattern, es fehlt ein Word
                            match = BrainLocator.GetMatch();
                            match.MatchType = MatchType.Missing;
                            match.Pattern = pToken;
                            match.Word = null;
                            matches.Add(match);
                            ti--;
                            continue;
                        }
                    }
                   
                    if(ti < textToken.Count)
                    {
                        //Pattern LookAhead
                        match = diff(pToken, textToken[ti]);
                        if (match != null)
                        {
                            //nächstes Token passt zu  Pattern, es fehlt ein Word
                            match = BrainLocator.GetMatch();
                            match.MatchType = MatchType.Extra;
                            match.Pattern = null;
                            match.Word = tToken;
                            matches.Add(match);
                            pi--;
                            continue;
                        }
                    }



                    match = BrainLocator.GetMatch();
                    match.MatchType = pToken.Type.Equals(tToken.Type) ? MatchType.Different : MatchType.DifferentType;
                    match.Pattern = pToken;
                    match.Word = tToken;
                    matches.Add(match);

                }

          
               
            }





            return matches;
        }

        static string RemoveDiacritics(string text)
        {
            var normalizedString = text.Normalize(NormalizationForm.FormD);
            var stringBuilder = new StringBuilder();

            foreach (var c in normalizedString)
            {
                var unicodeCategory = CharUnicodeInfo.GetUnicodeCategory(c);
                if (unicodeCategory != UnicodeCategory.NonSpacingMark)
                {
                    stringBuilder.Append(c);
                }
            }

            return stringBuilder.ToString().Normalize(NormalizationForm.FormC);
        }

        private IMatch diff(ITextToken pToken, ITextToken tToken)
        {
            if (pToken.Type.Equals(tToken.Type))
            {
                if (pToken.Data.Equals(tToken.Data))
                {
                    var match = BrainLocator.GetMatch();
                    match.MatchType = MatchType.Equal;
                    match.Pattern = pToken;
                    match.Word = tToken;
                    return match;
                }

                if (pToken.Data.ToLower().Equals(tToken.Data.ToLower()))
                {
                    var match = BrainLocator.GetMatch();
                    match.MatchType = MatchType.EqualIgnoreCase;
                    match.Pattern = pToken;
                    match.Word = tToken;
                    return match;
                }
                if (RemoveDiacritics(pToken.Data.ToLower()).Equals(RemoveDiacritics(tToken.Data.ToLower())))
                {
                    var match = BrainLocator.GetMatch();
                    match.MatchType = MatchType.EqualIgnoreDiacritics;
                    match.Pattern = pToken;
                    match.Word = tToken;
                    return match;
                }
            }

            return null;
        }
    }
}
